#include "EditExtractionsWindow.h"

EditExtractionsWindow::EditExtractionsWindow(QWidget *parent) : QDialog(parent){
    setupUi(this);
    this->videoManager = VideoManagerSingleton::getInstance();
    this->saveWindow = new SaveWindow(this);

    this->playIcon = new QIcon();
    this->pauseIcon = new QIcon();
    this->playIcon->addFile(QString::fromUtf8(":/icons/icons/media-play.png"), QSize(), QIcon::Normal, QIcon::Off);
    this->pauseIcon->addFile(QString::fromUtf8(":/icons/icons/media-pause.png"), QSize(), QIcon::Normal, QIcon::Off);
    this->userChangingTimeEdit = true;

    this->startTimeEdit->setCurrentSectionIndex(2);
    this->endTimeEdit->setCurrentSectionIndex(2);

    this->timer = new QTimer();
    this->connect(this->timer, SIGNAL(timeout()), this, SLOT(updateReadingSliderAndTime()));

    this->connect(this->saveButton, SIGNAL(clicked()), this->saveWindow, SLOT(show()));
    this->connect(this->playButton, SIGNAL(clicked()), this, SLOT(playOrPause()));
    this->connect(this->removeExtractionButton, SIGNAL(clicked()), this, SLOT(removeSelectedExtraction()));
    this->connect(this->readingSlider, SIGNAL(sliderMoved(int)), this, SLOT(seek(int)));
    this->connect(this->extractionsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(selectExtraction(int)));

    this->connect(this->boundariesSpanSlider, SIGNAL(spanChanged(int,int)), this, SLOT(updateAfterSpanSliderChanged(int,int)));
    this->connect(this->startTimeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(updateAfterStartTimeEditsChanged(QTime)));
    this->connect(this->endTimeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(updateAfterEndTimeEditsChanged(QTime)));

    this->connect(this->videoManager, SIGNAL(newExtractionAdded(int, int)), this, SLOT(addExtraction(int, int)));
    this->connect(this->videoManager, SIGNAL(newVideoOpened()), this, SLOT(_resetExtractionView()));

    this->connect(this, SIGNAL(accepted()), this, SLOT(_pause()));
    this->connect(this->saveWindow, SIGNAL(accepted()), this, SLOT(save()));
    this->connect(this, SIGNAL(rejected()), this, SLOT(_pause()));
    this->connect(this->videoManager, SIGNAL(videoSaved()), this, SLOT(sayVideoSavedAndRenableWidgets()));
    this->connect(this->videoManager, SIGNAL(calculingExtraction()), this, SLOT(sayCalculingExtractionAndDiseableWidgets()));
    this->connect(this->videoManager, SIGNAL(newExtractionAdded(int,int)), this, SLOT(sayNothingAndEnableWidgets()));
    //this->connect(this->videoManager, SIGNAL(waveFormGot()), this, SLOT(sayNothingAndEnableWidgets()));

    //TODO add action when we close, accept or reject this window
}

EditExtractionsWindow::~EditExtractionsWindow(){
    this->timer->stop();
    delete this->timer;
    delete this->playIcon;
    delete this->pauseIcon;
}

void EditExtractionsWindow::closeEvent(QCloseEvent* ce ){
    Q_UNUSED(ce);
    this->_pause();
}

int EditExtractionsWindow::getVideoWidgetId(){
        return (int)this->videoWidget->winId();
}

void EditExtractionsWindow::selectFirstItem(){
    if(this->extractionsListWidget->count() > 0){
        this->extractionsListWidget->setCurrentRow(0);
    }
}

void EditExtractionsWindow::setPlayIcon(){
    this->playButton->setToolTip(tr("Lire"));
    this->playButton->setIcon(*this->playIcon);
}

void EditExtractionsWindow::setPauseIcon(){
    this->playButton->setToolTip(tr("Pause"));
    this->playButton->setIcon(*this->pauseIcon);
}

void EditExtractionsWindow::playOrPause(){
    if(this->videoManager->isPlayingExtractedVideo()){
        this->_pause();
    }else{
        this->_play();
    }
}

void EditExtractionsWindow::_pause(){
    this->videoManager->pauseExtractedVideo();
    emit pause();
}

void EditExtractionsWindow::_play(){
    this->videoManager->playExtractedVideo();
    if(this->videoManager->isPlayingExtractedVideo()){
        this->timer->start(REFRESH_SLIDER_TIME);
        emit play();
    }
}

Extraction EditExtractionsWindow::getCurrentExtraction(){
    return this->videoManager->getExtraction(this->getCurrentExtractionIndex());
}

void EditExtractionsWindow::addExtraction(int start, int end){
    //int nExtractions = this->videoManager->getNExtractions();
    //Extraction lastExtraction = this->videoManager->getLastExtraction();
    Extraction extraction;
    extraction.start = start;
    extraction.end = end;
    double fps = this->videoManager->getFps();
    QTime startTime(0, 0, 0);
    startTime = startTime.addSecs(extraction.start/fps);
    QTime endTime(0, 0, 0);
    endTime = endTime.addSecs(extraction.end/fps);
    QString format("mm:ss");
    int maxTimeInSec = this->videoManager->getMaxTimeMainVideo();
    if(maxTimeInSec / 3600 > 0){
        format = QString("hh:") + format;
    }
    QString extractionName(startTime.toString(format) + " => " + endTime.toString(format));
    this->extractionsListWidget->addItem(extractionName);
    if(this->extractionsListWidget->count() == 1){
        this->selectFirstItem();
    }
}

void EditExtractionsWindow::addAllExtractions(){
    int nExtractions = this->videoManager->getNExtractions();
    double fps = this->videoManager->getFps();
    for(int i=0; i<nExtractions; i++){
        Extraction extraction = this->videoManager->getExtraction(i);
        QTime startTime(0, 0, 0);
        startTime = startTime.addSecs(extraction.start/fps);
        QTime endTime(0, 0, 0);
        endTime = endTime.addSecs(extraction.end/fps);
        QString format("mm:ss");
        int maxTimeInSec = this->videoManager->getMaxTimeMainVideo();
        if(maxTimeInSec / 3600 > 0){
            format = QString("hh:") + format;
        }
        QString extractionName(startTime.toString(format) + " => " + endTime.toString(format));
        this->extractionsListWidget->addItem(extractionName);
    }
}
void EditExtractionsWindow::removeSelectedExtraction(){
    int idSelectedExtraction = this->getCurrentExtractionIndex();
    delete this->extractionsListWidget->currentItem();
    this->videoManager->removeExtraction(idSelectedExtraction);
}

void EditExtractionsWindow::_resetExtractionView(){
    this->extractionsListWidget->clear();
}

int EditExtractionsWindow::getCurrentExtractionIndex(){
    return this->extractionsListWidget->currentIndex().row();
}

void EditExtractionsWindow::seek(int position){
    this->videoManager->seekExtractedVideo(position);
}

void EditExtractionsWindow::updateReadingSliderAndTime(){
    this->updateReadingSlider();
    this->updateTime();
}

void EditExtractionsWindow::updateReadingSlider(){
    double fps = this->videoManager->getFps();
    int maximum = this->getCurrentExtraction().end;
    if(!this->readingSlider->isSliderDown() && this->videoManager->isPlayingExtractedVideo()){
        int sliderValue = this->readingSlider->value();
        int currentPosition = sliderValue + fps*REFRESH_SLIDER_TIME/1000.0;
        if(currentPosition <= maximum){
            this->readingSlider->setValue(currentPosition); //TODO understand why this go to the maximum and then come back...may be a wrong seek
        }else{
            this->_pause();
            this->readingSlider->setValue(maximum);
        }
    }
}

void EditExtractionsWindow::updateTime(){
    int currentTimeInSec = this->videoManager->getCurrentTimeExtractedVideo();
    QTime currentTime(0, 0, 0);
    currentTime = currentTime.addSecs(currentTimeInSec);
    QString format("mm:ss");
    if(currentTimeInSec / 3600 > 0){
        format = QString("hh:") + format;
    }
    this->readingTimeLabel->setText(currentTime.toString(format));
}

void EditExtractionsWindow::selectExtraction(int id){
    if(id >= 0){
        this->videoManager->selectExtraction(id);
        this->userChangingTimeEdit = false;
        this->_updateTimeEditValues();
        this->userChangingTimeEdit = true;
        this->_updateSpanSliderBoundaries();
        this->_updateSpanSliderValues();
        this->_updateReadingSliderBoundaries();
        this->readingSlider->setValue(this->getCurrentExtraction().start);
        this->updateTime();
    }
}

void EditExtractionsWindow::updateAfterSpanSliderChanged(int lowerValue, int upperValue){
    if(this->boundariesSpanSlider->isSliderDown()){
        this->_changeCurrentExtractionValues(lowerValue, upperValue);
        this->_updateCurrentExtractionText();
        this->_updateSpanSliderBoundaries();
        this->userChangingTimeEdit = false;
        this->_updateTimeEditValues();
        this->userChangingTimeEdit = true;
        this->_updateReadingSliderBoundaries();
        this->_eventuallyUpdateExtractedVideoPosition();
    }
}

void EditExtractionsWindow::updateAfterStartTimeEditsChanged(QTime startTime){
    if(this->userChangingTimeEdit){
        int startInSec = QTime(0, 0, 0).secsTo(startTime);
        double fps = this->videoManager->getFps();
        int startInFrame = startInSec*fps;
        int endInFrame = this->getCurrentExtraction().end;
        this->_changeCurrentExtractionValues(startInFrame, endInFrame);
        this->_updateCurrentExtractionText();
        this->_updateSpanSliderBoundaries();
        this->_updateSpanSliderValues();
        this->_updateReadingSliderBoundaries();
        this->_eventuallyUpdateExtractedVideoPosition();
    }
}

void EditExtractionsWindow::updateAfterEndTimeEditsChanged(QTime endTime)
{
    if(this->userChangingTimeEdit){
        int endInSec = QTime(0, 0, 0).secsTo(endTime);
        double fps = this->videoManager->getFps();
        int startInFrame =this->getCurrentExtraction().start;
        int endInFrame = endInSec * fps;
        this->_changeCurrentExtractionValues(startInFrame, endInFrame);
        this->_updateCurrentExtractionText();
        this->_updateSpanSliderBoundaries();
        this->_updateSpanSliderValues();
        this->_updateReadingSliderBoundaries();
        this->_eventuallyUpdateExtractedVideoPosition();
    }
}
void EditExtractionsWindow::_updateSpanSliderBoundaries(){
    Extraction extraction = this->getCurrentExtraction();
    int nMaxSecToAdd = 60;
    int minimum = extraction.start - nMaxSecToAdd;
    if(minimum < 0){
        minimum = 0;
    }
    int nFrames = this->videoManager->getNFrames();
    int maximum = extraction.end + nMaxSecToAdd;
    if(maximum > nFrames){
        maximum = nFrames;
    }
    this->boundariesSpanSlider->setMinimum(minimum);
    this->boundariesSpanSlider->setMaximum(maximum);
}

void EditExtractionsWindow::_updateSpanSliderValues(){
    Extraction extraction = this->getCurrentExtraction();
    if(extraction.start < this->boundariesSpanSlider->upperValue()){
        this->boundariesSpanSlider->setLowerValue(extraction.start);
        this->boundariesSpanSlider->setUpperValue(extraction.end);
    }else{
        this->boundariesSpanSlider->setUpperValue(extraction.end);
        this->boundariesSpanSlider->setLowerValue(extraction.start);

    }
}

void EditExtractionsWindow::_updateTimeEditValues(){
    Extraction extraction = this->getCurrentExtraction();
    double fps = this->videoManager->getFps();
    this->startTimeEdit->setTime(QTime(0, 0, 0).addSecs(extraction.start/fps));
    this->endTimeEdit->setTime(QTime(0, 0, 0).addSecs(extraction.end/fps));
}

void EditExtractionsWindow::_updateReadingSliderBoundaries(){
    Extraction extraction = this->getCurrentExtraction();
    this->readingSlider->setMinimum(extraction.start);
    this->readingSlider->setMaximum(extraction.end);
}

void EditExtractionsWindow::_eventuallyUpdateExtractedVideoPosition(){
    int currentPosition = this->videoManager->getCurrentPositionExtractedVideo();
    Extraction currentExtraction = this->getCurrentExtraction();
    if(currentPosition < currentExtraction.start){
        this->videoManager->seekExtractedVideo(currentExtraction.start);
        this->readingSlider->setValue(currentExtraction.start);
    }else if(currentPosition > currentExtraction.end){
        this->videoManager->seekExtractedVideo(currentExtraction.end);
        this->readingSlider->setValue(currentExtraction.end);
    }
}

void EditExtractionsWindow::_changeCurrentExtractionValues(int start, int end){
    Extraction extraction;
    extraction.start = start;
    extraction.end = end;
    this->videoManager->replaceExtraction(this->getCurrentExtractionIndex(), extraction);
}

void EditExtractionsWindow::_updateCurrentExtractionText(){
    Extraction extraction = this->getCurrentExtraction();
    int fps = this->videoManager->getFps();
    QTime startTime(0, 0, 0);
    startTime = startTime.addSecs(extraction.start/fps);
    QTime endTime(0, 0, 0);
    endTime = endTime.addSecs(extraction.end/fps);
    QString format("mm:ss");
    int maxTimeInSec = this->videoManager->getMaxTimeMainVideo();
    if(maxTimeInSec / 3600 > 0){
        format = QString("hh:") + format;
    }
    QString extractionName(startTime.toString(format) + " => " + endTime.toString(format));
    this->extractionsListWidget->currentItem()->setText(extractionName);
}

void EditExtractionsWindow::sayVideoSavedAndRenableWidgets(){
    this->infoLabel->setText(tr("Vidéo sauvegardé."));
    this->setEnabled(true);
}

void EditExtractionsWindow::saySavingVideo(){
    this->infoLabel->setText(tr("Sauvegarder de la vidéo en cours..."));
}

void EditExtractionsWindow::sayCalculingExtractionAndDiseableWidgets(){
    this->infoLabel->setText(tr("Extraction des séquences..."));
    this->setEnabled(false);
}

void EditExtractionsWindow::sayNothingAndEnableWidgets(){
    this->infoLabel->setText("");
    this->setEnabled(true);
}

void EditExtractionsWindow::save(){
    /*
    QProgressDialog progressDialog;
    progressDialog.setVisible(true);
    progressDialog.setLabelText(tr("Enregistrement en cours..."));
    progressDialog.setRange(0, 100);
    progressDialog.setValue(10);
    progressDialog.setWindowTitle(tr("Enregistrement"));
    progressDialog.setWindowIcon(QIcon(":/icons/application.png"));
    QCoreApplication::processEvents (QEventLoop::AllEvents);
    //*/
    //this->infoLabel->setText(tr("Encodage en cours"));
    //TODO Find out why the text isn't print on to the progressDialog at the begining
    //TODO Find out how to only let the user click on the progressDialog
    this->saySavingVideo();
    this->setEnabled(false);
    this->videoManager->saveExtractions(this->saveWindow->getOutFileName());

    //progressDialog.setValue(100);
    //progressDialog.setVisible(false);
    //this->infoLabel->setText(tr("Encodage terminé"));
    //QMessageBox::information(this, tr("Enregistrement"), tr("Fichier enregistré avec succès !"));

}

